package com.meeting.meetingroom_api.service.impl;

import com.meeting.meetingroom_api.constants.ErrorMessages;
import com.meeting.meetingroom_api.dto.BookingDto;
import com.meeting.meetingroom_api.entity.Booking;
import com.meeting.meetingroom_api.entity.MeetingRoom;
import com.meeting.meetingroom_api.entity.Payment;
import com.meeting.meetingroom_api.enums.BookingStatus;
import com.meeting.meetingroom_api.enums.PaymentStatus;
import com.meeting.meetingroom_api.enums.RoomStatus;
import com.meeting.meetingroom_api.repository.BookingRepository;
import com.meeting.meetingroom_api.repository.MeetingRoomRepository;
import com.meeting.meetingroom_api.repository.PaymentRepository;
import com.meeting.meetingroom_api.service.MaintenanceRefundService;
import jakarta.persistence.EntityNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class MaintenanceRefundServiceImpl implements MaintenanceRefundService {

    private static final Logger logger = LoggerFactory.getLogger(MaintenanceRefundServiceImpl.class);
    private static final int REFUND_HOURS_BEFORE = 24; // 提前24小时退款

    private final BookingRepository bookingRepository;
    private final MeetingRoomRepository meetingRoomRepository;
    private final PaymentRepository paymentRepository;

    @Autowired
    public MaintenanceRefundServiceImpl(
            BookingRepository bookingRepository,
            MeetingRoomRepository meetingRoomRepository,
            PaymentRepository paymentRepository) {
        this.bookingRepository = bookingRepository;
        this.meetingRoomRepository = meetingRoomRepository;
        this.paymentRepository = paymentRepository;
    }

    @Override
    @Scheduled(fixedRate = 3600000) // 每小时执行一次
    @Transactional
    public void processMaintenanceRefunds() {
        logger.info("开始处理维护中会议室的自动退款...");

        // 查找所有维护中的会议室
        List<MeetingRoom> maintenanceRooms = meetingRoomRepository.findByStatus(RoomStatus.maintenance);
        
        int refundCount = 0;
        for (MeetingRoom room : maintenanceRooms) {
            List<BookingDto> conflictBookings = checkMaintenanceConflicts(room.getRoomId());
            for (BookingDto bookingDto : conflictBookings) {
                try {
                    processMaintenanceRefund(bookingDto.getBookingId(), 
                        "会议室维护中，系统自动全额退款");
                    refundCount++;
                    logger.info("成功处理维护退款 - 预订ID: {}, 会议室: {}", 
                        bookingDto.getBookingId(), room.getName());
                } catch (Exception e) {
                    logger.error("处理维护退款失败 - 预订ID: {}, 错误: {}", 
                        bookingDto.getBookingId(), e.getMessage());
                }
            }
        }

        logger.info("维护退款处理完成，共处理 {} 笔退款", refundCount);
    }

    @Override
    public List<BookingDto> checkMaintenanceConflicts(Integer roomId) {
        MeetingRoom room = meetingRoomRepository.findById(roomId)
                .orElseThrow(() -> new EntityNotFoundException(String.format(ErrorMessages.MEETING_ROOM_NOT_FOUND, roomId)));

        if (room.getStatus() != RoomStatus.maintenance) {
            return List.of(); // 会议室不在维护状态，无需退款
        }

        // 查找该会议室在未来24小时内的确认预订
        LocalDateTime cutoffTime = LocalDateTime.now().plusHours(REFUND_HOURS_BEFORE);
        
        Specification<Booking> spec = (root, query, cb) -> cb.and(
            cb.equal(root.get("room").get("roomId"), roomId),
            cb.equal(root.get("status"), BookingStatus.confirmed),
            cb.greaterThan(root.get("startDatetime"), LocalDateTime.now()),
            cb.lessThanOrEqualTo(root.get("startDatetime"), cutoffTime)
        );

        List<Booking> conflictBookings = bookingRepository.findAll(spec);
        return conflictBookings.stream()
                .map(this::convertToDto)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public BookingDto processMaintenanceRefund(Integer bookingId, String reason) {
        Booking booking = bookingRepository.findById(bookingId)
                .orElseThrow(() -> new EntityNotFoundException("找不到预订ID: " + bookingId));

        // 验证预订状态
        if (booking.getStatus() != BookingStatus.confirmed) {
            throw new IllegalStateException(ErrorMessages.MAINTENANCE_REFUND_NOT_CONFIRMED);
        }

        // 验证会议室状态
        if (booking.getRoom().getStatus() != RoomStatus.maintenance) {
            throw new IllegalStateException(ErrorMessages.ROOM_NOT_UNDER_MAINTENANCE);
        }

        // 验证时间条件
        LocalDateTime cutoffTime = LocalDateTime.now().plusHours(REFUND_HOURS_BEFORE);
        if (booking.getStartDatetime().isAfter(cutoffTime)) {
            throw new IllegalStateException(ErrorMessages.REFUND_TIME_NOT_MET);
        }

        // 处理退款
        Payment payment = paymentRepository.findByBooking(booking)
                .orElseThrow(() -> new EntityNotFoundException(ErrorMessages.PAYMENT_NOT_FOUND));

        if (payment.getStatus() != PaymentStatus.successful) {
            throw new IllegalStateException(ErrorMessages.PAYMENT_STATUS_INCORRECT);
        }

        // 更新支付状态为已退款
        payment.setStatus(PaymentStatus.refunded);
        payment.setPaymentGatewayResponse(reason);
        paymentRepository.save(payment);

        // 更新预订状态为系统取消
        booking.setStatus(BookingStatus.cancelled_by_customer); // 或者创建新的状态
        booking.setRoomConditionNotes(reason);
        booking.setUpdatedAt(LocalDateTime.now());
        
        Booking updatedBooking = bookingRepository.save(booking);

        logger.info("维护退款处理成功 - 预订ID: {}, 金额: {}, 原因: {}", 
            bookingId, booking.getTotalPrice(), reason);

        return convertToDto(updatedBooking);
    }

    @Override
    public Page<BookingDto> getPendingMaintenanceRefunds(Pageable pageable) {
        // 查找所有维护中的会议室
        List<MeetingRoom> maintenanceRooms = meetingRoomRepository.findByStatus(RoomStatus.maintenance);
        
        if (maintenanceRooms.isEmpty()) {
            return new PageImpl<>(List.of(), pageable, 0);
        }

        // 提取会议室ID
        List<Integer> roomIds = maintenanceRooms.stream()
                .map(MeetingRoom::getRoomId)
                .collect(Collectors.toList());

        // 查找需要退款的预订
        LocalDateTime cutoffTime = LocalDateTime.now().plusHours(REFUND_HOURS_BEFORE);
        
        Specification<Booking> spec = (root, query, cb) -> cb.and(
            root.get("room").get("roomId").in(roomIds),
            cb.equal(root.get("status"), BookingStatus.confirmed),
            cb.greaterThan(root.get("startDatetime"), LocalDateTime.now()),
            cb.lessThanOrEqualTo(root.get("startDatetime"), cutoffTime)
        );

        Page<Booking> bookingsPage = bookingRepository.findAll(spec, pageable);
        return bookingsPage.map(this::convertToDto);
    }

    private BookingDto convertToDto(Booking booking) {
        if (booking == null) {
            return null;
        }

        BookingDto dto = new BookingDto();
        dto.setBookingId(booking.getBookingId());
        dto.setStartDatetime(booking.getStartDatetime());
        dto.setEndDatetime(booking.getEndDatetime());
        dto.setAttendeesCount(booking.getAttendeesCount());
        dto.setEquipmentRequired(booking.getEquipmentRequired());
        dto.setTotalPrice(booking.getTotalPrice());
        dto.setStatus(booking.getStatus());
        dto.setPaymentDeadline(booking.getPaymentDeadline());
        dto.setCheckInTime(booking.getCheckInTime());
        dto.setCheckOutTime(booking.getCheckOutTime());
        dto.setRoomConditionNotes(booking.getRoomConditionNotes());
        dto.setCreatedAt(booking.getCreatedAt());
        dto.setUpdatedAt(booking.getUpdatedAt());
        
        if (booking.getCustomer() != null) {
            dto.setCustomerId(booking.getCustomer().getUserId());
            dto.setCustomerName(booking.getCustomer().getFullName());
        }
        
        if (booking.getRoom() != null) {
            dto.setRoomId(booking.getRoom().getRoomId());
            dto.setRoomName(booking.getRoom().getName());
        }
        
        if (booking.getCheckedBy() != null) {
            dto.setCheckedById(booking.getCheckedBy().getUserId());
            dto.setCheckedByName(booking.getCheckedBy().getFullName());
        }
        
        return dto;
    }
} 